package com.xzit.rental.security;

import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
// 开启注解权限控制
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableMethodSecurity
public class SecurityConfig {

    @Resource
    private VerifyTokenFilter verifyTokenFilter;

    /*依赖注入部分*/
    // 用于从数据源（如数据库等）加载用户的详细信息（包括用户名、密码、权限等），
    // 供 Spring Security 在进行用户认证时使用，以验证用户提供的登录凭证是否合法。
    @Resource
    private CustomerUserDetailsService customerUserDetailsService;
    @Resource
    private LoginSuccessHandler loginSuccessHandler;
    @Resource
    private LoginFailerHandler loginFailerHandler;
    // 当用户未进行认证（比如未登录却访问需要认证的资源）时触发的入口处理类，用于向客户端返回相应的提示信息，告知需要先进行认证等情况。
    @Resource
    private AuthenticationEntryPoint authenticationEntryPoint;
    // 在用户访问资源但权限不足时发挥作用，负责向客户端返回表示权限不足的提示信息等，前面也有相关类实现该功能并在这里被注入使用。
    @Resource
    private AccessDeniedHandler accessDeniedHandler;

    /**
     * @Description: 配置SecurityFilterChain过滤器链
     */
    @Bean
    public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity httpSecurity) throws Exception {

        //自定义过滤器放在UsernamePasswordAuthenticationFilter过滤器之前
        httpSecurity.addFilterBefore(verifyTokenFilter, UsernamePasswordAuthenticationFilter.class);

        // 用户详情服务配置
        httpSecurity.userDetailsService(customerUserDetailsService);

        // 登录过程处理
        // 开启表单登录相关配置
        httpSecurity.formLogin()
                .loginProcessingUrl("/rental/user/login") //设置处理用户登录请求的具体 URL 路径
                .successHandler(loginSuccessHandler)
                .failureHandler(loginFailerHandler);
        // 请求授权配置
        // 开始配置请求的授权规则
        httpSecurity.authorizeHttpRequests()
                .requestMatchers("/rental/user/login")
                // .permitAll()表示上一行的路径的请求是被允许的，不需要进行认证就能访问，相当于对登录接口进行了放行操作，方便用户能正常提交登录信息。
                // .anyRequest().authenticated()表示除了前面放行的登录接口之外的其他任何请求，都需要进行认证，只有认证通过的用户才能访问，以此来保护应用中的其他资源不被未授权用户访问。
                        .permitAll().anyRequest().authenticated();
        // 异常处理配置
        // 开启异常处理相关配置
        httpSecurity.exceptionHandling()
                // 用户未进行认证却访问需要认证的资源时
                .authenticationEntryPoint(authenticationEntryPoint)
                // 用户访问资源权限不足（已经认证但没有对应权限）
                .accessDeniedHandler(accessDeniedHandler);
        //禁用session
        httpSecurity.sessionManagement(AbstractHttpConfigurer::disable);
        //禁用csrf保护
        httpSecurity.csrf(AbstractHttpConfigurer::disable);
        httpSecurity.cors(corsCongfig-> {
            corsCongfig.configurationSource(corsCongfig());
        });

        //构建并返回配置好的SecurityFilterChain对象，这个对象会被 Spring 容器管理，
        // 并且在处理 Web 请求时会按照配置好的安全逻辑对请求进行过滤和处理，保障应用的安全访问。
        return httpSecurity.build();
    }

    private CorsConfigurationSource corsCongfig() {
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.addAllowedOriginPattern("*");
        corsConfiguration.addAllowedHeader("*");
        corsConfiguration.addAllowedMethod("*");
        corsConfiguration.setAllowCredentials(true);
        corsConfiguration.setMaxAge(3600L);
        UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
        urlBasedCorsConfigurationSource.registerCorsConfiguration("/**",corsConfiguration);
        return urlBasedCorsConfigurationSource;

    }
}
